package com.sanyue.springboot;

import com.sanyue.springboot.bean.Dog;
import com.sanyue.springboot.config.Config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * 本包及其子包的内容为run方法的源码分析
 * @author: Guan
 **/
@SpringBootApplication
public class MainApp {
    public static void main(String[] args) {
        //提出问题：当我们执行run方法时，如何启动内置的Tomcat
        //需求：在分析run方法的底层机制基础上，我们尝试自己实现
        /**
         * 我们开始Debug SpringApplication.run()
         *  1.SpringApplication.java
         *    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
         *         return run(new Class[]{primarySource}, args);
         *     }
         *  2.SpringApplication.java
         *    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
         *           return (new SpringApplication(primarySources)).run(args);
         *    }
         *  3.SpringApplication.java
         *  public ConfigurableApplicationContext run(String... args){
         *    注意：选取重要部分
         *    3.1 context = this.createApplicationContext(); 创建容器
         *      3.1.1 SpringApplication.java
         *      protected ConfigurableApplicationContext createApplicationContext() {
         *         return this.applicationContextFactory.create(this.webApplicationType);
         *     }
         *     解读：
         *     根据webApplicationType的类型创造对应的容器
         *     this.webApplicationType——默认为Servlet，也就是web容器，处理servelet
         *     3.1.2 ApplicationContextFactory.java
         *     @FunctionalInterface
         *     public interface ApplicationContextFactory {
         *          ApplicationContextFactory DEFAULT = (webApplicationType) -> {
         *              try {
         *                  switch (webApplicationType) {
         *                      case SERVLET:
         *                          return new AnnotationConfigServletWebServerApplicationContext();
         *                      case REACTIVE:
         *                          return new AnnotationConfigReactiveWebServerApplicationContext();
         *                      default:
         *                          return new AnnotationConfigApplicationContext();
         *                  }
         *              } catch (Exception var2) {
         *                  throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
         *              }
         *     };
         *     解读：
         *      如果SERVLET类型，就创建AnnotationConfigServletWebServerApplicationContext()类型的容器
         *    3.2 this.refreshContext(context); 刷新应用上下文路径，理解为刷新环境（准备应用程序的运行环境）
         *                                  如初始化默认设置、Tomcat的启动、注入相关的bean
         *                                  我们查看context对象的信息，singleTonObject无bean的信息，所以还没进行配置bean等操作
         *       3.2.1 SpringApplication.class
         *       private void refreshContext(ConfigurableApplicationContext context) {
         *         if (this.registerShutdownHook) {
         *             shutdownHook.registerApplicationContext(context);
         *         }
         *         this.refresh(context);
         *       }
         *       解读：this.refresh(context);是核心
         *       3.2.2 SpringApplication.class
         *       protected void refresh(ConfigurableApplicationContext applicationContext) {
         *          applicationContext.refresh();
         *       }
         *       3.2.3 ServletWebServerApplicationContext.class
         *       public final void refresh() throws BeansException, IllegalStateException {
         *               super.refresh();核心方法
         *       }
         *       解读：super：AnnotationConfigServletWebServerApplicationContext是父类，
         *                   对应3.1——this.createApplicationContext()中创建的AnnotationConfigServletWebServerApplicationContext();
         *       3.2.4 AbstractApplicationContext.java
         *       @Override
         *         public void refresh() throws BeansException, IllegalStateException {
         * 		 synchronized (this.startupShutdownMonitor) {
         * 		 	StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
         *
         * 		 	// Prepare this context for refreshing.
         * 		 	prepareRefresh();
         *
         * 		 	// Tell the subclass to refresh the internal bean factory.
         * 		 	ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
         *
         * 		 	// Prepare the bean factory for use in this context.
         * 		 	prepareBeanFactory(beanFactory);
         *
         * 			 try {
         * 			 	// Allows post-processing of the bean factory in context subclasses.
         * 			 	postProcessBeanFactory(beanFactory);
         *
         * 			 	StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
         * 			 	// Invoke factory processors registered as beans in the context.
         * 			 	invokeBeanFactoryPostProcessors(beanFactory);
         *
         * 				// Register bean processors that intercept bean creation.
         * 				 registerBeanPostProcessors(beanFactory);
         * 				beanPostProcess.end();
         *
         * 				// Initialize message source for this context.
         * 				initMessageSource();
         *
         * 				// Initialize event multicaster for this context.
         * 				initApplicationEventMulticaster();
         *
         * 				// Initialize other special beans in specific context subclasses.
         * 				onRefresh(); 核心
         *
         * 				// Check for listener beans and register them.
         * 				registerListeners();
         *
         * 				// Instantiate all remaining (non-lazy-init) singletons.
         * 				finishBeanFactoryInitialization(beanFactory);
         *
         * 				// Last step: publish corresponding event.
         * 				finishRefresh();
         *            }
         *
         * 			catch (BeansException ex) {
         * 				if (logger.isWarnEnabled()) {
         * 					logger.warn("Exception encountered during context initialization - " +
         * 							"cancelling refresh attempt: " + ex);
         *                }
         *
         * 				// Destroy already created singletons to avoid dangling resources.
         * 				destroyBeans();
         *
         * 				// Reset 'active' flag.
         * 				cancelRefresh(ex);
         *
         * 				// Propagate exception to caller.
         * 				throw ex;
         *            }
         *
         * 			finally {
         * 				// Reset common introspection caches in Spring's core, since we
         * 				// might not ever need metadata for singleton beans anymore...
         * 				resetCommonCaches();
         * 				contextRefresh.end();
         *            }
         *        }
         *    }
         *  }
         *       解读：onRefresh(); 当父类完成通用工作后，再重新通过动态绑定机制返回到子类
         *
         *       3.2.5 ServletWebServerApplicationContext.class
         *       protected void onRefresh() {
         *         super.onRefresh();
         *         try {
         *             this.createWebServer(); 核心代码
         *         } catch (Throwable var2) {
         *             throw new ApplicationContextException("Unable to start web server", var2);
         *         }
         *     }
         *     解读： this.createWebServer();创建webServer，就可以理解为创建Tomcat
         *
         *     3.2.6 ServletWebServerApplicationContext.class
         *     private void createWebServer() {
         *         WebServer webServer = this.webServer;
         *         ServletContext servletContext = this.getServletContext();
         *         if (webServer == null && servletContext == null) {
         *             StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
         *             ServletWebServerFactory factory = this.getWebServerFactory();
         *             createWebServer.tag("factory", factory.getClass().toString());
         *             this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); 核心
         *             createWebServer.end();
         *             this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
         *             this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
         *         } else if (servletContext != null) {
         *             try {
         *                 this.getSelfInitializer().onStartup(servletContext);
         *             } catch (ServletException var5) {
         *                 throw new ApplicationContextException("Cannot initialize servlet context", var5);
         *             }
         *         }
         *
         *         this.initPropertySources();
         *     }
         *     解读：
         *      1.this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); 核心
         *      使用TomcatServletWebServerFactory创建一个TomcatWebServer
         *      2.factory：TomcatServletWebServer类
         *
         *     3.2.7 TomcatServletWebServerFactory.class：创建Tomcat，并启动Tomcat
         *     public WebServer getWebServer(ServletContextInitializer... initializers) {
         *         if (this.disableMBeanRegistry) {
         *             Registry.disableRegistry();
         *         }
         *
         *         Tomcat tomcat = new Tomcat();    //创建Tomcat
         *         File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
         *         //做了一系列设置
         *         tomcat.setBaseDir(baseDir.getAbsolutePath());
         *         Connector connector = new Connector(this.protocol);
         *         connector.setThrowOnFailure(true);
         *         tomcat.getService().addConnector(connector);
         *         this.customizeConnector(connector);
         *         tomcat.setConnector(connector);
         *         tomcat.getHost().setAutoDeploy(false);
         *         this.configureEngine(tomcat.getEngine());
         *         Iterator var5 = this.additionalTomcatConnectors.iterator();
         *
         *         while(var5.hasNext()) {
         *             Connector additionalConnector = (Connector)var5.next();
         *             tomcat.getService().addConnector(additionalConnector);
         *         }
         *
         *         this.prepareContext(tomcat.getHost(), initializers);
         *         return this.getTomcatWebServer(tomcat);
         *     }
         *     接下来：
         *     return this.getTomcatWebServer(tomcat);核心
         *
         *
         *     3.2.8 TomcatServletWebServerFactory.class
         *     protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
         *         return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
         *     }
         *
         *     3.2.9 TomcatWebServer.class
         *     public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
         *         this.monitor = new Object();
         *         this.serviceConnectors = new HashMap();
         *         Assert.notNull(tomcat, "Tomcat Server must not be null");
         *         this.tomcat = tomcat;
         *         this.autoStart = autoStart;
         *         this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
         *         this.initialize();
         *     }
         *     接下来：this.initialize();
         *     3.3.10 TomcatWebServer.class
         *     private void initialize() throws WebServerException {
         *         logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
         *         synchronized(this.monitor) {
         *             try {
         *                 this.addInstanceIdToEngineName();
         *                 Context context = this.findContext();
         *                 context.addLifecycleListener((event) -> {
         *                     if (context.equals(event.getSource()) && "start".equals(event.getType())) {
         *                         this.removeServiceConnectors();
         *                     }
         *
         *                 });
         *                 this.tomcat.start();
         *                 this.rethrowDeferredStartupExceptions();
         *
         *                 try {
         *                     ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
         *                 } catch (NamingException var5) {
         *                 }
         *
         *                 this.startDaemonAwaitThread();
         *             } catch (Exception var6) {
         *                 this.stopSilently();
         *                 this.destroySilently();
         *                 throw new WebServerException("Unable to start embedded Tomcat", var6);
         *             }
         *
         *         }
         *     }
         *     解读：
         *      this.tomcat.start();Tomcat启动成功
         *      这时，我们回去看context = this.createApplicationContext();发现context的bean已经成功注入
         *
         */
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);

        //测试环境
        Dog dog = ioc.getBean(Dog.class);
        Config bean = ioc.getBean(Config.class);
        System.out.println("dog = " + dog);
        System.out.println("bean = " + bean);

    }

}
